use std::task::{Context, Poll};

use actix_http::Method;
use actix_session::UserSession;
use actix_web::{
    dev::{Service, ServiceRequest, ServiceResponse, Transform},
    Error,
};
use futures::future::LocalBoxFuture;
use wither::bson::oid::ObjectId;

use crate::{
    errors::cons_error, models::user::NormalUserModel, services::user_service::UserService,
    utils::authorize::check_user_has_permission_with_object_id, AppError,
};
use log::info;
use std::rc::Rc;

pub mod club_getters {
    use std::str::FromStr;

    use wither::bson::oid::ObjectId;

    use crate::utils::strings::parse_url_query_params;

    use super::*;
    /// ## default_getter
    /// 默认的getter，立刻返回空
    pub fn default_getter<T>(_: &T) -> Option<ObjectId> {
        None
    }

    pub fn url_getter<T: Into<String>>(key: T) -> impl Fn(&ServiceRequest) -> Option<ObjectId> {
        let key_name: String = key.into();
        move |s: &ServiceRequest| {
            parse_url_query_params(s.query_string()).find_map(|(k, value)| {
                if key_name == *k {
                    ObjectId::from_str(&value).ok()
                } else {
                    None
                }
            })
        }
    }

    pub fn path_getter<T: Into<String>>(key: T) -> impl Fn(&ServiceRequest) -> Option<ObjectId> {
        let key_name: String = key.into();
        move |s: &ServiceRequest| {
            s.match_info()
                .get(key_name.as_str())
                .map(ObjectId::from_str)
                .and_then(Result::ok)
        }
    }
}
/// ## Authorize
/// 鉴权中间件，对session中的user进行权限判断
/// 传入的club_getter将用于对club进行特殊判断
pub struct Authorize<F>(Inner<F>)
where
    F: Fn(&ServiceRequest) -> Option<ObjectId> + 'static;

impl<F> Authorize<F>
where
    F: Fn(&ServiceRequest) -> Option<ObjectId> + 'static,
{
    pub fn new<T: Into<u8>>(role: T, club_getter: F) -> Self
    where
        F: Fn(&ServiceRequest) -> Option<ObjectId>,
    {
        let role = role.into();
        Self(Inner {
            role,
            club_getter: Some(Rc::new(club_getter)),
            methods: vec![],
        })
    }
    pub fn method<M: Into<Method>>(self, m: M) -> Self {
        Self(Inner {
            methods: {
                let mut nm = vec![m.into()];
                nm.extend(self.0.methods);
                nm
            },
            ..self.0
        })
    }
    pub fn empty_method(self) -> Self {
        Self(Inner {
            methods: vec![],
            ..self.0
        })
    }
}
#[derive(Debug)]
struct Inner<F>
where
    F: Fn(&ServiceRequest) -> Option<ObjectId> + 'static,
{
    role: u8,
    club_getter: Option<Rc<F>>,
    methods: Vec<Method>,
}
impl<T> Clone for Inner<T>
where
    T: Fn(&ServiceRequest) -> Option<ObjectId> + 'static,
{
    fn clone(&self) -> Self {
        Self {
            role: self.role,
            club_getter: self.club_getter.clone(),
            methods: self.methods.clone(),
        }
    }
}

impl<S, B, F> Transform<S, ServiceRequest> for Authorize<F>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    S::Future: 'static,
    B: 'static,
    F: Fn(&ServiceRequest) -> Option<ObjectId> + 'static,
{
    type Response = ServiceResponse<B>;
    type Error = S::Error;
    type Transform = AuthorizeMiddleware<S, F>;
    type InitError = ();
    type Future = LocalBoxFuture<'static, Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, s: S) -> Self::Future {
        let inner = self.0.clone();
        Box::pin(async move { Ok(AuthorizeMiddleware { service: s, inner }) })
    }
}

pub struct AuthorizeMiddleware<S: 'static, F>
where
    F: Fn(&ServiceRequest) -> Option<ObjectId> + 'static,
{
    service: S,
    inner: Inner<F>,
}

impl<S, B, F> Service<ServiceRequest> for AuthorizeMiddleware<S, F>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    S::Future: 'static,
    B: 'static,
    F: Fn(&ServiceRequest) -> Option<ObjectId> + 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }
    fn call(&self, req: ServiceRequest) -> Self::Future {
        let req_url = req.path();
        let method = req.method();
        {
            if !self.inner.methods.is_empty() && !self.inner.methods.contains(method) {
                let future = self.service.call(req);
                return Box::pin(async move { future.await });
            }
        }
        {
            let user = match UserService::get_user_from_session(&req.get_session()) {
                Ok(u) => u,
                Err(err) => {
                    info!("{} {} not found use in session", req_url, method);
                    return Box::pin(async move { Err(err.into()) });
                }
            };

            let club_id = self.inner.club_getter.clone().and_then(|v| (*v)(&req)); // 调用一下回调获取一下请求中的club_id

            if !check_user_has_permission_with_object_id(
                NormalUserModel::from(user.clone()),
                club_id,
                self.inner.role,
            ) {
                info!("{} {} user_id: {:?} unauthorized", req_url, method, user.id);
                return Box::pin(async {
                    Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE).into())
                });
            }
        }
        let future = self.service.call(req);
        Box::pin(async move { future.await })
    }
}
